﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Windows;
using ActiveStory.DataManager;
using ActiveStory.IO;
using System.IO;
using System.Windows.Controls;

namespace ActiveStory
{
    public static class ActiveStoryApplication
    {
#region Constants
        public const string ProjectFileExtension = ".aseprojx";
        public const string PageFileExtension = ".asepgx";
        public const string ButtonToPageMapFileExtension = ".asebtpx";
        public const string PageLookupTableFileExtension = ".aseptx";
        public const string XamlFileExtension = ".xaml";
        public const string ImageFileExtension = ".png";
        public const string ProjectSilverlightExportExtension = ".slprojx";
        public const string PageSilverlightExportExtension = ".slpgx";
        public const string ButtonDetailsSilverlightExtension = ".slbdx";
        public static string TempFolder = Path.Combine(System.IO.Path.GetTempPath(), @"ActiveStory Enhanced\");
        public static System.Drawing.Imaging.ImageFormat SavedImageFormat = System.Drawing.Imaging.ImageFormat.Png;
        public enum ServerHostType { BuiltIn, External, Error };
        public enum ImageFormat { PNG, GIF, JPG, BMP, TIFF, Error };
#endregion

        public static ActiveStoryProject CurrentProject = null;
        public static ActiveStoryPage CurrentPage = null;
        public static Point CurrentCanvasSize = new Point();

        public static void Init()
        {
            if (Directory.Exists(TempFolder))
            {
                CleanTempFiles();
            }
            else
            {
                Directory.CreateDirectory(TempFolder);
            }
        }

        public static void Exit()
        {
            Application.Current.Shutdown();
            System.Diagnostics.Process.GetCurrentProcess().Kill();
        }

        private static void CleanTempFiles()
        {
            if (Directory.Exists(TempFolder))
            {
                string[] files = Directory.GetFiles(TempFolder);
                foreach (String item in files)
                {
                    File.Delete(item);
                }
            }
        }

        public static void LoadProjectInstance(string projectFile)
        {
            FileManager.ProjectPath = Path.GetDirectoryName(projectFile);
            CurrentProject = new ActiveStoryProject(projectFile);
            CurrentCanvasSize = CurrentProject.DefaultCanvasSize;
        }

        public static void CreateProjectInstance(string projectName, string projectFolder, Point defaultCanvasSize)
        {
            FileManager.ProjectPath = projectFolder;
            CurrentProject = new ActiveStoryProject(projectName, FileManager.CreateProjectFileName(projectName,projectFolder), defaultCanvasSize);
        }

        public static void SaveProject()
        {
            CurrentProject.SaveFile();
        }

        public static void NewPage(System.Windows.Controls.InkCanvas inkCanvas)
        {
            if (CurrentPage != null)
            {
                SaveCurrentPage(inkCanvas);
            }
            ClearPage(inkCanvas);
            CurrentProject.IncrementPageCounter();
            int pageNumber = CurrentProject.PageCounter;
            CurrentPage = new ActiveStoryPage("Page" + (pageNumber+1), pageNumber, FileManager.CreateXamlFileName(CurrentProject.ProjectName, pageNumber), FileManager.CreateThumbnailFileName(CurrentProject.ProjectName, pageNumber));
            CurrentCanvasSize = CurrentProject.DefaultCanvasSize;
            Border inkCanvasParent = (Border)inkCanvas.Parent;
            inkCanvasParent.Width = CurrentCanvasSize.X;
            inkCanvasParent.Height = CurrentCanvasSize.Y;
        }

        private static void ClearPage(System.Windows.Controls.InkCanvas inkCanvas)
        {
            inkCanvas.Strokes.Clear();
            inkCanvas.Children.Clear();
        }

        public static void SaveCurrentPage(System.Windows.Controls.InkCanvas inkCanvas)
        {
            ExportXaml(inkCanvas, CurrentPage.XamlFileLocation);
            ExportThumbnail(inkCanvas, CurrentPage.ThumbnailFileLocation);
            string pageFileName = FileManager.CreatePageFileName(CurrentProject.ProjectName, CurrentPage.PageNumber);
            ActiveStoryPageMetadata.SavePage(CurrentProject.ProjectName, pageFileName, CurrentPage, CurrentPage.XamlFileLocation, CurrentPage.ThumbnailFileLocation);
            CurrentProject.AddPage(CurrentPage.PageNumber, pageFileName);
        }

        private static void ExportXaml(System.Windows.Controls.InkCanvas inkCanvas, string filename)
        {
            FileStream fs = new FileStream(FileManager.GetAbsolutePath(filename), FileMode.Create);
            inkCanvas.Width = CurrentCanvasSize.X;
            inkCanvas.Height = CurrentCanvasSize.Y;
            System.Windows.Markup.XamlWriter.Save(inkCanvas, fs);
            inkCanvas.Width = double.NaN;
            inkCanvas.Height = double.NaN;
            fs.Close();
        }

        public static void ImportXaml(ref System.Windows.Controls.InkCanvas inkCanvas, string filename)
        {
            FileStream fs = new FileStream(FileManager.GetAbsolutePath(filename), FileMode.Open);
            System.Windows.Controls.InkCanvas tempInkCanvas= (System.Windows.Controls.InkCanvas)System.Windows.Markup.XamlReader.Load(fs);
            fs.Close();
            if (inkCanvas.Parent != null)
            {
                Border tempInkCanvasBorder = (Border)inkCanvas.Parent;
                tempInkCanvasBorder.Width = tempInkCanvas.Width;
                tempInkCanvasBorder.Height = tempInkCanvas.Height;
                CurrentCanvasSize.X = tempInkCanvas.Width;
                CurrentCanvasSize.Y = tempInkCanvas.Height;
            }
            inkCanvas.Strokes.Add(tempInkCanvas.Strokes);
            while(tempInkCanvas.Children.Count>0)
            {
                UIElement item = tempInkCanvas.Children[0];
                tempInkCanvas.Children.Remove(item);
                inkCanvas.Children.Add(item);
            }
        }

        private static void ExportThumbnail(System.Windows.Controls.InkCanvas inkCanvas, string filename)
        {
            foreach (UIElement item in inkCanvas.Children)
            {
                if (item.GetType() != typeof(Button))
                {
                    continue;
                }
                Button tempButton = (Button)item;
                tempButton.Visibility = Visibility.Hidden;
            }
            System.Windows.Media.Imaging.RenderTargetBitmap rtb = new System.Windows.Media.Imaging.RenderTargetBitmap((int)CurrentCanvasSize.X, (int)CurrentCanvasSize.Y, 96d, 96d, System.Windows.Media.PixelFormats.Default);
            rtb.Render(inkCanvas);
            System.Windows.Media.Imaging.PngBitmapEncoder encoder = new System.Windows.Media.Imaging.PngBitmapEncoder();
            encoder.Frames.Add(System.Windows.Media.Imaging.BitmapFrame.Create(rtb));
            FileStream fs = new FileStream(FileManager.GetAbsolutePath(filename), FileMode.Create);
            encoder.Save(fs);
            fs.Flush();
            fs.Close();
            foreach (UIElement item in inkCanvas.Children)
            {
                if (item.GetType() != typeof(Button))
                {
                    continue;
                }
                Button tempButton = (Button)item;
                tempButton.Visibility = Visibility.Visible;
            }
        }

        private static System.Windows.Controls.Image ImportThumbnail(string filename)
        {
            System.Windows.Controls.Image result = new System.Windows.Controls.Image();
            result.Source = new System.Windows.Media.Imaging.BitmapImage(new Uri(FileManager.GetAbsolutePath(filename)));
            return result;
        }

        public static void SelectPage(System.Windows.Controls.InkCanvas inkCanvas, int pageNumber, ActiveStory.UI.MainWindow windowInstance)
        {
            if (CurrentPage != null)
            {
                if (inkCanvas.Children.Count > 0 || inkCanvas.Strokes.Count > 0)
                {
                    SaveCurrentPage(inkCanvas);
                    ClearPage(inkCanvas);
                }
            }
            CurrentPage = ActiveStoryPageMetadata.LoadPage(CurrentProject.PageLookupTable[pageNumber]);
            ImportXaml(ref inkCanvas, CurrentPage.XamlFileLocation);
            HookupEvents(ref inkCanvas, windowInstance);
        }

        private static void HookupEvents(ref System.Windows.Controls.InkCanvas inkCanvas, ActiveStory.UI.MainWindow windowInstance)
        {
            
            foreach (UIElement item in inkCanvas.Children)
            {
                if (item.GetType() != typeof(Button))
                    continue;
                Button buttonItem = (Button)item;
                if (CurrentPage.ButtonToPageMap.ContainsKey((int)buttonItem.Content))
                {
                    int tempPageNumber= CurrentPage.ButtonToPageMap[(int)buttonItem.Content];
                    if (!CurrentProject.PageLookupTable.ContainsKey(tempPageNumber))
                    {
                        CurrentPage.RemoveButtonMapping(tempPageNumber);
                        buttonItem.ToolTip = "Not linked to any page.";
                    }
                    else
                        buttonItem.ToolTip = "Linked to page " + (CurrentPage.ButtonToPageMap[(int)buttonItem.Content] + 1);
                }
                else
                    buttonItem.ToolTip = "Not linked to any page.";

                buttonItem.Click += new RoutedEventHandler(delegate(object sender2, RoutedEventArgs e2)
                {
                    ActiveStory.UI.LinkDialog ld;
                    if (windowInstance.CurrentPenMode == ActiveStory.UI.MainWindow.PenMode.RemoveLink)
                    {
                        if (ActiveStoryApplication.CurrentPage.ButtonToPageMap.ContainsKey((int)buttonItem.Content))
                            ld = new ActiveStory.UI.LinkDialog(ActiveStory.UI.LinkDialog.LinkDialogModes.RemoveLink, ActiveStoryApplication.CurrentPage.ButtonToPageMap[(int)buttonItem.Content]);
                        else
                        {
                            MessageBox.Show(windowInstance, "This link is not associated with any page.", "Error, no link.", MessageBoxButton.OK, MessageBoxImage.Error);
                            return;
                        }
                    }
                    else if (!ActiveStoryApplication.CurrentPage.ButtonToPageMap.ContainsKey((int)buttonItem.Content))
                        ld = new ActiveStory.UI.LinkDialog(ActiveStory.UI.LinkDialog.LinkDialogModes.CreateLink, -1);
                    else
                    {
                        int tempPageNumber = CurrentPage.ButtonToPageMap[(int)buttonItem.Content];
                        if (!CurrentProject.PageLookupTable.ContainsKey(tempPageNumber))
                        {
                            CurrentPage.RemoveButtonMapping(tempPageNumber);
                            ld = new ActiveStory.UI.LinkDialog(ActiveStory.UI.LinkDialog.LinkDialogModes.CreateLink, -1);
                        }
                        else
                            ld = new ActiveStory.UI.LinkDialog(ActiveStory.UI.LinkDialog.LinkDialogModes.Both, ActiveStoryApplication.CurrentPage.ButtonToPageMap[(int)buttonItem.Content]);
                    }
                    ld.Owner = windowInstance;
                    if (ld.ShowDialog() == false)
                        return;
                    int newTargetPageNum = ld.TargetPageNumber;
                    if (newTargetPageNum == -1)    //Removed link
                    {
                        ActiveStoryApplication.CurrentPage.RemoveButtonMapping((int)buttonItem.Content);
                        buttonItem.ToolTip = "Not linked to any page.";
                    }
                    else                            //Changed link
                    {
                        ActiveStoryApplication.CurrentPage.AddButtonMapping((int)buttonItem.Content, newTargetPageNum);
                        buttonItem.ToolTip = "Linked to page " + (newTargetPageNum + 1);
                    }
                }); 
            }

            
            
        }

        public static void ProjectChangeCleanup(System.Windows.Controls.InkCanvas inkCanvas)
        {
            CurrentPage = null;
            ClearPage(inkCanvas);
        }

        public static void RemovePage(System.Windows.Controls.InkCanvas inkCanvas )
        {
            if (CurrentPage == null)
            {
                NewPage(inkCanvas);
                return;
            }
            if (!CurrentProject.PageLookupTable.ContainsKey(CurrentPage.PageNumber))    //The page was never saved yet
            {
                CurrentPage = null;
                NewPage(inkCanvas);
                return;
            }
            string xamlFileLocation= FileManager.GetAbsolutePath(CurrentPage.XamlFileLocation);
            string thumbFileLocation=FileManager.GetAbsolutePath(CurrentPage.ThumbnailFileLocation);
            string buttonMapFileLocation = FileManager.GetAbsolutePath(CurrentProject.PageLookupTable[CurrentPage.PageNumber].ButtonToPageMapFileLocation);
            string pageFileLocation = FileManager.GetAbsolutePath(CurrentProject.PageLookupTable[CurrentPage.PageNumber].PageFileLocation);
            CurrentProject.RemovePage(CurrentPage.PageNumber);
            CurrentPage = null;
            NewPage(inkCanvas);
            try
            {
                File.Delete(xamlFileLocation);
            }
            catch (Exception exc)
            {
#if DEBUG
                throw exc;
#endif
            }
            try
            {
                File.Delete(thumbFileLocation);
            }
            catch (Exception exc)
            {
#if DEBUG
                throw exc;
#endif
            }
            try
            {
                File.Delete(buttonMapFileLocation);
            }
            catch (Exception exc)
            {
#if DEBUG          
                throw exc;
#endif
            }
            try
            {
                File.Delete(pageFileLocation);
            }
            catch (Exception exc)
            {
#if DEBUG
                throw exc;
#endif
            }
        }

        public static void ExportToSilverlightWizardOfOz(string absoluteExportPath, ServerHostType selectedServerHostType, int startupPage, string taskInstructions)
        {
            int port = -1;
            if (selectedServerHostType == ServerHostType.BuiltIn)
            {
                port = int.Parse(absoluteExportPath);
                absoluteExportPath = Path.Combine(Path.GetDirectoryName(System.Reflection.Assembly.GetExecutingAssembly().Location), @"SilverlightWoZ\WoZData");
            }
                    //Directory.CreateDirectory((Path.GetTempPath() + @"\ActiveStoryWoZ")).FullName;
            ActiveStory.Exporter.ActiveStorySilverlightExporter silExport = new ActiveStory.Exporter.ActiveStorySilverlightExporter();
            if (selectedServerHostType == ServerHostType.External)
            {
                CopyWoZSiteToTargetDirectory(absoluteExportPath);
                absoluteExportPath = Path.Combine(absoluteExportPath, @"WoZData");
            }
            silExport.Export(CurrentProject, absoluteExportPath, startupPage, taskInstructions);
            if (selectedServerHostType == ServerHostType.BuiltIn)
            {
                    RegisterWithCassini(Path.Combine(Path.GetDirectoryName(System.Reflection.Assembly.GetExecutingAssembly().Location), @"SilverlightWoZ"), port);
            }
        }

        private static void RegisterWithCassini(string absoluteExportPath, int port)
        {
            CassiniConfiguration.ApplicationEntry ae = new CassiniConfiguration.ApplicationEntry(new Guid("3d5900ae-111a-45be-96b3-d9e4606ca793"), "WoZ", "", port, absoluteExportPath, "Default.aspx", true);
            CassiniConfiguration.Metabase metaBase = CassiniConfiguration.Metabase.Load();
            metaBase.RegisterApplication(ae, true);
            return;
        }

        private static void CopyWoZSiteToTargetDirectory(string absoluteExportPath)
        {
            FileManager.CopyDirectory(Path.Combine(Path.GetDirectoryName(System.Reflection.Assembly.GetExecutingAssembly().Location), @"SilverlightWoZ"), absoluteExportPath);
        }

        /// <summary>
        /// Exports the currently visible page to an image file given the absolute path,
        /// including the filename and extension as well as the requested image format
        /// </summary>
        /// <param name="absoluteExportPath"></param>
        /// <param name="formatToSave"></param>
        public static void ExportCurrentPageToImage(string absoluteExportPath, ImageFormat formatToSave)
        {
            if (formatToSave == ImageFormat.Error)
                return;
            if (!File.Exists(FileManager.GetAbsolutePath(CurrentPage.ThumbnailFileLocation)))
                return;
            ActiveStory.Exporter.ActiveStoryImageExporter imgExporter = new ActiveStory.Exporter.ActiveStoryImageExporter();
            imgExporter.Export(CurrentProject, absoluteExportPath, -1, CurrentPage.ThumbnailFileLocation, formatToSave.ToString());
        }

        /// <summary>
        /// Exports the all pages in the project to image files given the absolute path to the target directory,
        /// as well as the requested image format.
        /// </summary>
        /// <param name="absoluteExportPath"></param>
        /// <param name="formatToSave"></param>
        public static void ExportAllPageToImages(string absoluteExportPath, ImageFormat formatToSave)
        {
            if (formatToSave == ImageFormat.Error)
                return;
            ActiveStory.Exporter.ActiveStoryImageExporter imgExporter = new ActiveStory.Exporter.ActiveStoryImageExporter();
            imgExporter.Export(CurrentProject, absoluteExportPath, -1, formatToSave.ToString());
        }

        public static void SetCanvasSize(InkCanvas inkCanvas, Point canvasSize)
        {
            CurrentCanvasSize = canvasSize;
            Border tempInkCanvasBorder = (Border)inkCanvas.Parent;
            tempInkCanvasBorder.Height = canvasSize.Y;
            tempInkCanvasBorder.Width = canvasSize.X;
        }

        internal static void SetCurrentPageName(string newName)
        {
            CurrentPage.PageName = newName;
        }
    }
}
